///*
// * Created on 15-Nov-2004
// * Created by Paul Gardner
// * Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
// *
// * This program is free software; you can redistribute it and/or
// * modify it under the terms of the GNU General Public License
// * as published by the Free Software Foundation; either version 2
// * of the License, or (at your option) any later version.
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU General Public License for more details.
// * You should have received a copy of the GNU General Public License
// * along with this program; if not, write to the Free Software
// * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
// *
// * AELITIS, SAS au capital de 46,603.30 euros
// * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
// *
// */
//
package org.gudy.azureus2.core3.download.impl;

import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
//
//import org.gudy.azureus2.core3.category.Category;
//import org.gudy.azureus2.core3.category.CategoryManager;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
//import org.gudy.azureus2.core3.disk.DiskManagerFactory;
//import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.download.*;
//import org.gudy.azureus2.core3.logging.LogEvent;
//import org.gudy.azureus2.core3.logging.LogIDs;
//import org.gudy.azureus2.core3.logging.LogRelation;
//import org.gudy.azureus2.core3.logging.Logger;
//import org.gudy.azureus2.core3.peer.PEPeerSource;
import org.gudy.azureus2.core3.torrent.*;
//import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
import org.gudy.azureus2.core3.util.*;
//
//import com.aelitis.azureus.core.AzureusCoreFactory;
//import com.aelitis.azureus.core.util.CaseSensitiveFileMap;
//import com.aelitis.azureus.core.util.CopyOnWriteList;
//import com.aelitis.azureus.core.util.CopyOnWriteMap;
//
///**
// * @author parg
// * Overall aim of this is to stop updating the torrent file itself and update something
// * Azureus owns. To this end a file based on torrent hash is created in user-dir/active
// * It is actually just a copy of the torrent file
// */
//
public class 
DownloadManagerStateImpl
	implements ParameterListener //DownloadManagerState,
{

    @Override
    public void parameterChanged(String parameterName) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
//	private static final int	VER_INCOMING_PEER_SOURCE	= 1;
//	private static final int	VER_CURRENT					= VER_INCOMING_PEER_SOURCE;
//
//
//	private static final LogIDs LOGID = LogIDs.DISK;
//	private static final String			RESUME_KEY				= "resume";
//	private static final String			TRACKER_CACHE_KEY		= "tracker_cache";
//	private static final String			ATTRIBUTE_KEY			= "attributes";
//	private static final String			AZUREUS_PROPERTIES_KEY	= "azureus_properties";
//
//	private static final File			ACTIVE_DIR;
//
//	public static boolean SUPPRESS_FIXUP_ERRORS = false;
//
//	static{
//
//		ACTIVE_DIR = FileUtil.getUserFile( "active" );
//
//		if ( !ACTIVE_DIR.exists()){
//
//			FileUtil.mkdirs(ACTIVE_DIR);
//		}
//	}
//
//	private static Random	random = RandomUtils.SECURE_RANDOM;
//
//	private static final Map	default_parameters;
//	private static final Map	default_attributes;
//
//	static{
//		default_parameters  = new HashMap();
//
//		for (int i=0;i<PARAMETERS.length;i++){
//
//			default_parameters.put( PARAMETERS[i][0], PARAMETERS[i][1] );
//		}
//
//		default_attributes  = new HashMap();
//
//		for (int i=0;i<ATTRIBUTE_DEFAULTS.length;i++){
//
//			default_attributes.put( ATTRIBUTE_DEFAULTS[i][0], ATTRIBUTE_DEFAULTS[i][1] );
//		}
//
//		// only add keys that will point to Map objects here!
//		TorrentUtils.registerMapFluff( new String[] {TRACKER_CACHE_KEY,RESUME_KEY} );
//	}
//
//	private static AEMonitor	class_mon	= new AEMonitor( "DownloadManagerState:class" );
//
//	private static Map					state_map 					= new HashMap();
//	private static Map					global_state_cache			= new HashMap();
//	private static ArrayList			global_state_cache_wrappers	= new ArrayList();
//
//	private DownloadManagerImpl			download_manager;
//
//	private final TorrentUtils.ExtendedTorrent	torrent;
//
//	private boolean						write_required;
//
//	private Category 	category;
//
//	private CopyOnWriteList		listeners_cow	= new CopyOnWriteList();
//
//	private CopyOnWriteMap listeners_read_map_cow  = new CopyOnWriteMap();
//	private CopyOnWriteMap listeners_write_map_cow = new CopyOnWriteMap();
//
//
//	private Map			parameters;
//	private Map			attributes;
//
//	private AEMonitor	this_mon	= new AEMonitor( "DownloadManagerState" );
//
//	private boolean firstPrimaryFileRead = true;
//
//	private int supressWrites = 0;
//
//	private static ThreadLocal		tls_wbr	=
//		new ThreadLocal()
//		{
//			public Object
//			initialValue()
//			{
//				return( new ArrayList(1));
//			}
//		};
//
//	private static DownloadManagerState
//	getDownloadState(
//		DownloadManagerImpl				download_manager,
//		TOTorrent						original_torrent,
//		TorrentUtils.ExtendedTorrent	target_torrent )
//
//		throws TOTorrentException
//	{
//		byte[]	hash	= target_torrent.getHash();
//
//		DownloadManagerStateImpl	res	= null;
//
//		try{
//			class_mon.enter();
//
//			HashWrapper	hash_wrapper = new HashWrapper( hash );
//
//			res = (DownloadManagerStateImpl)state_map.get(hash_wrapper);
//
//			if ( res == null ){
//
//				res = new DownloadManagerStateImpl( download_manager, target_torrent );
//
//				state_map.put( hash_wrapper, res );
//
//			}else{
//
//					// if original state was created without a download manager,
//					// bind it to this one
//
//				if ( res.getDownloadManager() == null && download_manager != null ){
//
//					res.setDownloadManager( download_manager );
//				}
//
//				if ( original_torrent != null ){
//
//					res.mergeTorrentDetails( original_torrent );
//				}
//			}
//		}finally{
//
//			class_mon.exit();
//		}
//
//		return( res );
//	}
//
//
//	public static DownloadManagerState
//	getDownloadState(
//		TOTorrent		original_torrent )
//
//		throws TOTorrentException
//	{
//		byte[]	torrent_hash = original_torrent.getHash();
//
//		// System.out.println( "getDownloadState: hash = " + ByteFormatter.encodeString(torrent_hash));
//
//		TorrentUtils.ExtendedTorrent saved_state	= null;
//
//		File	saved_file = getStateFile( torrent_hash );
//
//		if ( saved_file.exists()){
//
//			try{
//				saved_state = TorrentUtils.readDelegateFromFile( saved_file, false );
//
//			}catch( Throwable e ){
//
//				Debug.out( "Failed to load download state for " + saved_file, e );
//			}
//		}
//
//			// if saved state not found then recreate from original torrent
//
//		if ( saved_state == null ){
//
//			TorrentUtils.copyToFile( original_torrent, saved_file );
//
//			saved_state = TorrentUtils.readDelegateFromFile( saved_file, false );
//		}
//
//		return( getDownloadState( null, original_torrent, saved_state ));
//	}
//
//	protected static DownloadManagerState
//	getDownloadState(
//		DownloadManagerImpl	download_manager,
//		String				torrent_file,
//		byte[]				torrent_hash,
//		boolean				inactive )
//
//		throws TOTorrentException
//	{
//		boolean	discard_pieces = state_map.size() > 32;
//
//		// System.out.println( "getDownloadState: hash = " + (torrent_hash==null?"null":ByteFormatter.encodeString(torrent_hash) + ", file = " + torrent_file ));
//
//		TOTorrent						original_torrent	= null;
//		TorrentUtils.ExtendedTorrent 	saved_state			= null;
//
//			// first, if we already have the hash then see if we can load the saved state
//
//		if ( torrent_hash != null ){
//
//			File	saved_file = getStateFile( torrent_hash );
//
//			if ( saved_file.exists()){
//
//				try{
//					Map	cached_state = (Map)global_state_cache.remove( new HashWrapper( torrent_hash ));
//
//					if ( cached_state != null ){
//
//						CachedStateWrapper wrapper = new CachedStateWrapper( download_manager, torrent_file, torrent_hash, cached_state, inactive );
//
//						global_state_cache_wrappers.add( wrapper );
//
//						saved_state	= wrapper;
//
//					}else{
//
//						saved_state = TorrentUtils.readDelegateFromFile( saved_file, discard_pieces );
//					}
//
//				}catch( Throwable e ){
//
//					Debug.out( "Failed to load download state for " + saved_file );
//				}
//			}
//		}
//
//			// if saved state not found then recreate from original torrent if required
//
//		if ( saved_state == null ){
//
//			original_torrent = TorrentUtils.readDelegateFromFile( new File(torrent_file), discard_pieces );
//
//			torrent_hash = original_torrent.getHash();
//
//			File	saved_file = getStateFile( torrent_hash );
//
//			if ( saved_file.exists()){
//
//				try{
//					saved_state = TorrentUtils.readDelegateFromFile( saved_file, discard_pieces );
//
//				}catch( Throwable e ){
//
//					Debug.out( "Failed to load download state for " + saved_file );
//				}
//			}
//
//			if ( saved_state == null ){
//
//					// we must copy the torrent as we want one independent from the
//					// original (someone might still have references to the original
//					// and do stuff like write it somewhere else which would screw us
//					// up)
//
//				TorrentUtils.copyToFile( original_torrent, saved_file );
//
//				saved_state = TorrentUtils.readDelegateFromFile( saved_file, discard_pieces );
//			}
//		}
//
//		DownloadManagerState res = getDownloadState( download_manager, original_torrent, saved_state );
//
//		if ( inactive ){
//
//			res.setActive( false );
//		}
//
//		return( res );
//	}
//
//	protected static File
//	getStateFile(
//		byte[]		torrent_hash )
//	{
//		return( new File( ACTIVE_DIR, ByteFormatter.encodeString( torrent_hash ) + ".dat" ));
//	}
//
//	protected static File
//	getGlobalStateFile()
//	{
//		return( new File( ACTIVE_DIR, "cache.dat" ));
//	}
//
//	public static void
//	loadGlobalStateCache()
//	{
//		File file = getGlobalStateFile();
//
//		if ( !file.canRead()){
//
//			return;
//		}
//
//		try{
//
//			BufferedInputStream is = new BufferedInputStream( new GZIPInputStream( new FileInputStream( file )));
//
//			try{
//
//				Map	map = BDecoder.decode( is );
//
//				List	cache = (List)map.get( "state" );
//
//				if ( cache != null ){
//
//					for (int i=0;i<cache.size();i++){
//
//						Map	entry = (Map)cache.get(i);
//
//						byte[]	hash = (byte[])entry.get( "hash" );
//
//						if ( hash != null ){
//
//							global_state_cache.put( new HashWrapper( hash ), entry );
//						}
//					}
//				}
//
//				is.close();
//
//			}catch( IOException e){
//
//				Debug.printStackTrace( e );
//			}finally{
//
//				try{
//					is.close();
//
//				}catch( Throwable e ){
//				}
//			}
//		}catch( Throwable e ){
//
//			Debug.printStackTrace( e );
//		}
//	}
//
//	public static void
//	saveGlobalStateCache()
//	{
//		try{
//			class_mon.enter();
//
//			Map	map = new HashMap();
//
//			List	cache = new ArrayList();
//
//			map.put( "state", cache );
//
//			Iterator	it = state_map.values().iterator();
//
//			while( it.hasNext()){
//
//				DownloadManagerState dms = (DownloadManagerState)it.next();
//
//				DownloadManager dm = dms.getDownloadManager();
//
//				if ( dm != null && dm.isPersistent()){
//
//					try{
//						Map	state = CachedStateWrapper.export( dms );
//
//						cache.add( state );
//
//					}catch( Throwable e ){
//
//						Debug.printStackTrace( e );
//					}
//				}
//			}
//
//			GZIPOutputStream	os = new GZIPOutputStream( new FileOutputStream( getGlobalStateFile()));
//
//			try{
//
//				os.write( BEncoder.encode( map ));
//
//				os.close();
//
//			}catch( IOException e ){
//
//				Debug.printStackTrace( e );
//
//				try{
//					os.close();
//
//				}catch( IOException f ){
//
//				}
//			}
//		}catch( Throwable e ){
//
//			Debug.printStackTrace( e );
//
//		}finally{
//
//			class_mon.exit();
//
//		}
//	}
//
//	public static void
//	discardGlobalStateCache()
//	{
//		getGlobalStateFile().delete();
//
//		for ( int i=0;i<global_state_cache_wrappers.size();i++){
//
//			((CachedStateWrapper)global_state_cache_wrappers.get(i)).clearCache();
//		}
//
//		global_state_cache_wrappers.clear();
//		global_state_cache_wrappers.trimToSize();
//	}
//
//	protected
//	DownloadManagerStateImpl(
//		DownloadManagerImpl				_download_manager,
//		TorrentUtils.ExtendedTorrent	_torrent )
//	{
//		download_manager	= _download_manager;
//		torrent				= _torrent;
//
//		attributes = torrent.getAdditionalMapProperty( ATTRIBUTE_KEY );
//
//		if ( attributes == null ){
//
//			attributes	= new HashMap();
//        }
//
//        String cat_string = getStringAttribute( AT_CATEGORY );
//
//        if ( cat_string != null ){
//
//        	Category cat = CategoryManager.getCategory( cat_string );
//
//        	if ( cat != null ){
//
//        		setCategory( cat );
//        	}
//        }
//
//        parameters	= getMapAttribute( AT_PARAMETERS );
//
//        if ( parameters == null ){
//
//        	parameters	= new HashMap();
//        }
//
//        	// note that version will be -1 for the first time through this code
//
//        int	version = getIntAttribute( AT_VERSION );
//
//        if ( version < VER_INCOMING_PEER_SOURCE ){
//
//        		// migrate by adding incoming as enabled - only needed if we have any specified as other
//        		// code takes care of the case where we have none
//
//        	if ( getPeerSources().length > 0 ){
//
//        		if ( PEPeerSource.isPeerSourceEnabledByDefault( PEPeerSource.PS_INCOMING )){
//
//        			setPeerSourceEnabled( PEPeerSource.PS_INCOMING, true );
//        		}
//        	}else{
//
//        			// set default for newly added torrent
//
//				setPeerSources( PEPeerSource.getDefaultEnabledPeerSources());
//        	}
//        }
//
//        if ( version < VER_CURRENT ){
//
//        	setIntAttribute( AT_VERSION, VER_CURRENT );
//        }
//
//        addListeners();
//	}
//
//	public void
//	parameterChanged(
//		String parameterName)
//	{
//			// get any listeners to pick up new values as their defaults are based on core params
//
//		informWritten( AT_PARAMETERS );
//	}
//
//	protected void
//	addListeners()
//	{
//		COConfigurationManager.addParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding", this );
//		COConfigurationManager.addParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding.Enable", this );
//		COConfigurationManager.addParameterListener( "Max.Peer.Connections.Per.Torrent", this );
//		COConfigurationManager.addParameterListener( "Max Uploads", this );
//		COConfigurationManager.addParameterListener( "Max Uploads Seeding", this );
//		COConfigurationManager.addParameterListener( "Max Seeds Per Torrent", this );
//		COConfigurationManager.addParameterListener( "enable.seedingonly.maxuploads", this );
//	}
//
//	protected void
//	removeListeners()
//	{
//		COConfigurationManager.removeParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding", this );
//		COConfigurationManager.removeParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding.Enable", this );
//		COConfigurationManager.removeParameterListener( "Max.Peer.Connections.Per.Torrent", this );
//		COConfigurationManager.removeParameterListener( "Max Uploads", this );
//		COConfigurationManager.removeParameterListener( "Max Uploads Seeding", this );
//		COConfigurationManager.removeParameterListener( "Max Seeds Per Torrent", this );
//		COConfigurationManager.removeParameterListener( "enable.seedingonly.maxuploads", this );
//	}
//
//	public DownloadManager
//	getDownloadManager()
//	{
//		return( download_manager );
//	}
//
//	protected void
//	setDownloadManager(
//		DownloadManagerImpl		dm )
//	{
//		download_manager	= dm;
//	}
//
//	public File
//	getStateFile( )
//	{
//		try{
//			File	parent = new File( ACTIVE_DIR, ByteFormatter.encodeString( torrent.getHash()) + File.separatorChar);
//
//			return( StringInterner.internFile(parent));
//
//		}catch( Throwable e ){
//
//			Debug.printStackTrace(e);
//
//			return( null );
//		}
//	}
//
//	public void
//	clearTrackerResponseCache()
//	{
//		setTrackerResponseCache( new HashMap());
//	}
//
//	public Map getTrackerResponseCache() {
//
//		Map tracker_response_cache = null;
//
//		tracker_response_cache = torrent.getAdditionalMapProperty(TRACKER_CACHE_KEY);
//
//		if (tracker_response_cache == null)
//			tracker_response_cache = new HashMap();
//
//		return (tracker_response_cache);
//	}
//
//	public void
//	setTrackerResponseCache(
//		Map		value )
//	{
//
//		try{
//			this_mon.enter();
//
//			// System.out.println( "setting download state/tracker cache for '" + new String(torrent.getName()));
//
//			boolean	changed = !BEncoder.mapsAreIdentical( value, getTrackerResponseCache() );
//
//			if ( changed ){
//
//				write_required	= true;
//
//				torrent.setAdditionalMapProperty( TRACKER_CACHE_KEY, value );
//			}
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	public Map
//	getResumeData()
//	{
//		try{
//			this_mon.enter();
//
//			return( torrent.getAdditionalMapProperty(RESUME_KEY));
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	public void
//	clearResumeData()
//	{
//		setResumeData( null );
//	}
//
//	public void
//	setResumeData(
//		Map	data )
//	{
//		try{
//			this_mon.enter();
//
//			// System.out.println( "setting download state/resume data for '" + new String(torrent.getName()));
//
//			if ( data == null ){
//
//				setLongAttribute( AT_RESUME_STATE, 1 );
//
//				torrent.removeAdditionalProperty( RESUME_KEY );
//
//			}else{
//
//				torrent.setAdditionalMapProperty( RESUME_KEY, data );
//
//				boolean complete = DiskManagerFactory.isTorrentResumeDataComplete( this );
//
//				setLongAttribute( AT_RESUME_STATE, complete?2:1 );
//			}
//
//			write_required	= true;
//
//		}finally{
//
//			this_mon.exit();
//		}
//
//			// we need to ensure this is persisted now as it has implications regarding crash restarts etc
//
//		save();
//	}
//
//	public boolean
//	isResumeDataComplete()
//	{
//			// this is a cache of resume state to speed up startup
//
//		long	state = getLongAttribute( AT_RESUME_STATE );
//
//		if ( state == 0 ){
//
//			// don't know
//
//			boolean complete = DiskManagerFactory.isTorrentResumeDataComplete( this );
//
//			setLongAttribute( AT_RESUME_STATE, complete?2:1 );
//
//			return( complete );
//
//		}else{
//
//			return( state == 2 );
//		}
//	}
//
//	public TOTorrent
//	getTorrent()
//	{
//		return( torrent );
//	}
//
//	public void
//	setActive(
//		boolean		active )
//	{
//		torrent.setDiscardFluff( !active );
//	}
//
//	public void discardFluff()
//	{
//		torrent.setDiscardFluff(true);
//	}
//
//	public void suppressStateSave(boolean suppress) {
//		if(suppress)
//			supressWrites++;
//		else if(supressWrites > 0)
//			supressWrites--;
//	}
//
//	public void
//	save()
//	{
//		if(supressWrites > 0)
//			return;
//
// 		boolean do_write;
//
//		try {
//			this_mon.enter();
//
//			do_write = write_required;
//
//			if(write_required != false)
//				write_required = false;
//
//		} finally {
//
//			this_mon.exit();
//		}
//
//		if ( do_write ){
//
//			try {
//				// System.out.println( "writing download state for '" + new String(torrent.getName()));
//
//				if (Logger.isEnabled())
//					Logger.log(new LogEvent(torrent, LOGID, "Saving state for download '"
//							+ TorrentUtils.getLocalisedName(torrent) + "'"));
//
//				torrent.setAdditionalMapProperty( ATTRIBUTE_KEY, attributes );
//
//				TorrentUtils.writeToFile(torrent, true);
//
//			} catch (Throwable e) {
//				Logger.log(new LogEvent(torrent, LOGID, "Saving state", e));
//			}
//		} else {
//
//			// System.out.println( "not writing download state for '" + new String(torrent.getName()));
//		}
//	}
//
//	public void
//	delete()
//	{
//		try{
//			class_mon.enter();
//
//			HashWrapper	wrapper = torrent.getHashWrapper();
//
//			state_map.remove( wrapper );
//
//	        TorrentUtils.delete( torrent );
//
//			File	dir = new File( ACTIVE_DIR, ByteFormatter.encodeString( wrapper.getBytes()));
//
//			if ( dir.exists() && dir.isDirectory()){
//
//				FileUtil.recursiveDelete( dir );
//			}
//
//			removeListeners();
//
//		}catch( Throwable e ){
//
//	    	Debug.printStackTrace( e );
//
//		}finally{
//
//			class_mon.exit();
//		}
//	}
//
//	protected void
//	mergeTorrentDetails(
//		TOTorrent	other_torrent )
//	{
//		try{
//			boolean	write = TorrentUtils.mergeAnnounceURLs( other_torrent, torrent );
//
//			// System.out.println( "DownloadManagerState:mergeTorrentDetails -> " + write );
//
//			if ( write ){
//
//				save();
//
//				if ( download_manager != null ){
//
//					TRTrackerAnnouncer	client = download_manager.getTrackerClient();
//
//					if ( client != null ){
//
//						// pick up any URL changes
//
//						client.resetTrackerUrl( false );
//					}
//				}
//			}
//		}catch( Throwable e ){
//
//			Debug.printStackTrace( e );
//		}
//	}
//
//	public void
//	setFlag(
//		long		flag,
//		boolean		set )
//	{
//		long	old_value = getLongAttribute( AT_FLAGS );
//
//		long	new_value;
//
//		if ( set ){
//
//			new_value = old_value | flag;
//
//		}else{
//
//			new_value = old_value & ~flag;
//		}
//
//		if ( old_value != new_value ){
//
//			setLongAttribute( AT_FLAGS, new_value );
//		}
//	}
//
//	public boolean
//	getFlag(
//		long	flag )
//	{
//		long	value = getLongAttribute( AT_FLAGS );
//
//		return(( value & flag ) != 0 );
//	}
//
//	public long
//	getFlags()
//	{
//		return( getLongAttribute( AT_FLAGS ));
//	}
//
//	public boolean parameterExists(String name) {
//		return parameters.containsKey(name);
//	}
//
//	public void
//	setParameterDefault(
//		String	name )
//	{
//		try{
//			this_mon.enter();
//
//			Object	value = parameters.get( name );
//
//			if ( value == null ){
//
//				return;
//			}
//
//				// gotta clone here otherwise we update the underlying  map and the setMapAttribute code
//				// doesn't think it has changed
//
//			parameters	= new LightHashMap(parameters);
//
//			parameters.remove( name );
//
//		}finally{
//
//			this_mon.exit();
//		}
//
//		setMapAttribute( AT_PARAMETERS, parameters );
//	}
//
//	public long
//	getLongParameter(
//		String	name )
//	{
//		try{
//			this_mon.enter();
//
//			Object	value = parameters.get( name );
//
//			if ( value == null ){
//
//				value = default_parameters.get( name );
//
//				if ( value == null ){
//
//					Debug.out( "Unknown parameter '" + name + "' - must be defined in DownloadManagerState" );
//
//					return( 0 );
//
//				}else{
//
//						// default overrides
//
//						// **** note - if you add to these make sure you extend the parameter listeners
//						// registered as well (see addParameterListeners)
//
//					if ( name == PARAM_MAX_UPLOADS_WHEN_SEEDING_ENABLED ){
//
//						if ( COConfigurationManager.getBooleanParameter( "enable.seedingonly.maxuploads" )){
//
//							value = new Boolean( true );
//						}
//
//					}else if ( name == PARAM_MAX_UPLOADS_WHEN_SEEDING ){
//
//						int	def = COConfigurationManager.getIntParameter( "Max Uploads Seeding" );
//
//						value = new Integer( def );
//
//					}else if ( name == PARAM_MAX_UPLOADS ){
//
//						int	def = COConfigurationManager.getIntParameter("Max Uploads" );
//
//						value = new Integer( def );
//
//					}else if ( name == PARAM_MAX_PEERS ){
//
//						int	def = COConfigurationManager.getIntParameter( "Max.Peer.Connections.Per.Torrent" );
//
//						value = new Integer( def );
//
//					}else if ( name == PARAM_MAX_PEERS_WHEN_SEEDING_ENABLED ){
//
//						if ( COConfigurationManager.getBooleanParameter( "Max.Peer.Connections.Per.Torrent.When.Seeding.Enable" )){
//
//							value = new Boolean( true );
//						}
//
//					}else if ( name == PARAM_MAX_PEERS_WHEN_SEEDING ){
//
//						int	def = COConfigurationManager.getIntParameter( "Max.Peer.Connections.Per.Torrent.When.Seeding" );
//
//						value = new Integer( def );
//
//					}else if ( name == PARAM_MAX_SEEDS ){
//
//						value = new Integer(COConfigurationManager.getIntParameter( "Max Seeds Per Torrent" ));
//
//					}else if ( name == PARAM_RANDOM_SEED ){
//
//						long	rand = random.nextLong();
//
//						setLongParameter( name, rand );
//
//						value = new Long( rand );
//					}
//				}
//			}
//
//			if ( value instanceof Boolean ){
//
//				return(((Boolean)value).booleanValue()?1:0);
//
//			}else if ( value instanceof Integer ){
//
//				return( ((Integer)value).longValue());
//
//			}else if ( value instanceof Long ){
//
//				return( ((Long)value).longValue());
//			}
//
//			Debug.out( "Invalid parameter value for '" + name + "' - " + value );
//
//			return( 0 );
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	public void
//	setLongParameter(
//		String		name,
//		long		value )
//	{
//		Object	default_value = default_parameters.get( name );
//
//		if ( default_value == null ){
//
//			Debug.out( "Unknown parameter '" + name + "' - must be defined in DownloadManagerState" );
//		}
//
//		try{
//			this_mon.enter();
//
//				// gotta clone here otherwise we update the underlying  map and the setMapAttribute code
//				// doesn't think it has changed
//
//			parameters	= new LightHashMap(parameters);
//
//			parameters.put( name, new Long(value));
//
//			setMapAttribute( AT_PARAMETERS, parameters );
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	public int
//	getIntParameter(
//		String	name )
//	{
//		return( (int)getLongParameter( name ));
//	}
//
//	public void
//	setIntParameter(
//		String	name,
//		int		value )
//	{
//		setLongParameter( name, value );
//	}
//
//	public boolean
//	getBooleanParameter(
//		String	name )
//	{
//		return( getLongParameter( name ) != 0 );
//	}
//
//	public void
//	setBooleanParameter(
//		String		name,
//		boolean		value )
//	{
//		setLongParameter( name, value?1:0 );
//	}
//
//	public void
//	setAttribute(
//		String		name,
//		String		value )
//	{
//
//		if ( name.equals( AT_CATEGORY )){
//
//			if ( value == null ){
//
//				setCategory( null );
//
//			}else{
//				Category	cat = CategoryManager.getCategory( value );
//
//				if ( cat == null ){
//
//					cat = CategoryManager.createCategory( value );
//
//				}
//
//				setCategory( cat );
//			}
//			return;
//		}
//
//		if (name.equals(AT_RELATIVE_SAVE_PATH)) {
//			if (value.length() > 0) {
//				File relative_path_file = new File(value);
//				relative_path_file = DownloadManagerDefaultPaths.normaliseRelativePath(relative_path_file);
//				value = (relative_path_file == null) ? "" : relative_path_file.getPath();
//			}
//		}
//
//		setStringAttribute( name, value );
//	}
//
//	public String
//	getAttribute(
//		String		name )
//	{
//		if ( name.equals( AT_CATEGORY )){
//
//			Category	cat = getCategory();
//
//			if ( cat == null ){
//
//				return( null );
//			}
//
//			if ( cat == CategoryManager.getCategory( Category.TYPE_UNCATEGORIZED )){
//
//				return( null );
//			}
//
//			return( cat.getName());
//
//		}else{
//
//			return( getStringAttribute( name ));
//		}
//	}
//
//	public
//	Category
//	getCategory()
//	{
//	    return category;
//	}
//
//	public void
//	setCategory(
//		Category 	cat )
//	{
//		if ( cat == CategoryManager.getCategory(Category.TYPE_UNCATEGORIZED)){
//
//			cat	= null;
//		}
//
//		if ( cat == category ){
//
//			return;
//		}
//
//		if (cat != null && cat.getType() != Category.TYPE_USER){
//
//			cat = null;
//		}
//
//		Category oldCategory = (category == null)?CategoryManager.getCategory(Category.TYPE_UNCATEGORIZED):category;
//
//		category = cat;
//
//		if (oldCategory != null ){
//
//			oldCategory.removeManager( this );
//  		}
//
//		if (category != null ){
//
//			category.addManager( this );
//		}
//
//		if ( category != null && category.getType() == Category.TYPE_USER ){
//
//			setStringAttribute( AT_CATEGORY, category.getName());
//
//		}else{
//
//			setStringAttribute( AT_CATEGORY, null );
//		}
//	}
//
//	public String
//	getTrackerClientExtensions()
//	{
//		return( getStringAttribute( AT_TRACKER_CLIENT_EXTENSIONS ));
//	}
//
//	public void
//	setTrackerClientExtensions(
//		String		value )
//	{
//		setStringAttribute( AT_TRACKER_CLIENT_EXTENSIONS, value );
//	}
//
//    public String getDisplayName() {
//    	return this.getStringAttribute(AT_DISPLAY_NAME);
//    }
//
//    public void setDisplayName(String value) {
//    	this.setStringAttribute(AT_DISPLAY_NAME, value);
//    }
//
//    public String getUserComment() {
//    	return this.getStringAttribute(AT_USER_COMMENT);
//    }
//
//    public void setUserComment(String value) {
//    	this.setStringAttribute(AT_USER_COMMENT, value);
//    }
//
//    public String getRelativeSavePath() {
//    	return this.getStringAttribute(AT_RELATIVE_SAVE_PATH);
//    }
//
//	public String getPrimaryFile() {
//		String sPrimary = this.getStringAttribute(AT_PRIMARY_FILE);
//		// Only recheck when file doesn't exists if this is the first check
//		// of the session, because the file may never exist and we don't want
//		// to continuously go through the fileinfos
//		if (sPrimary == null
//				|| sPrimary.length() == 0
//				|| (firstPrimaryFileRead && !new File(sPrimary).exists()
//						&& download_manager.getStats().getDownloadCompleted(true) != 0)) {
//			DiskManagerFileInfo[] fileInfo = download_manager.getDiskManagerFileInfo();
//			if (fileInfo.length > 0) {
//				int idxBiggest = -1;
//				long lBiggest = -1;
//				int numChecked = 0;
//				for (int i = 0; i < fileInfo.length && numChecked < 10; i++) {
//					if (!fileInfo[i].isSkipped()) {
//						numChecked++;
//						if (fileInfo[i].getLength() > lBiggest) {
//  						lBiggest = fileInfo[i].getLength();
//  						idxBiggest = i;
//						}
//					}
//				}
//				if (idxBiggest >= 0) {
//					sPrimary = fileInfo[idxBiggest].getFile(true).getPath();
//				}
//			}
//			// System.out.println("calc getPrimaryFile " + sPrimary + ": " + download_manager.getDisplayName());
//		}
//
//		if (sPrimary == null) {
//			sPrimary = "";
//		}
//
//		if (firstPrimaryFileRead) {
//			firstPrimaryFileRead = false;
//		}
//		setPrimaryFile(sPrimary);
//		return sPrimary;
//	}
//
//	/**
//	 * @param primary
//	 */
//	public void setPrimaryFile(String fileFullPath) {
//		this.setStringAttribute(AT_PRIMARY_FILE, fileFullPath);
//	}
//
//	public String[]
//	getNetworks()
//	{
//		List	values = getListAttributeSupport( AT_NETWORKS );
//
//		List	res = new ArrayList();
//
//			// map back to the constants to allow == comparisons
//
//		for (int i=0;i<values.size();i++){
//
//			String	nw = (String)values.get(i);
//
//			for (int j=0;j<AENetworkClassifier.AT_NETWORKS.length;j++){
//
//				String	nn = AENetworkClassifier.AT_NETWORKS[j];
//
//				if ( nn.equals( nw )){
//
//					res.add( nn );
//				}
//			}
//		}
//
//		String[]	x = new String[res.size()];
//
//		res.toArray(x);
//
//		return( x );
//	}
//
//	  public boolean isNetworkEnabled(
//	      String network) {
//	    List	values = getListAttributeSupport( AT_NETWORKS );
//	    return values.contains(network);
//	  }
//
//	public void
//	setNetworks(
//		String[]		networks )
//	{
//		if ( networks == null ){
//
//			networks = new String[0];
//		}
//
//		List	l = new ArrayList();
//
//		for (int i=0;i<networks.length;i++){
//
//			l.add( networks[i]);
//		}
//
//		setListAttribute( AT_NETWORKS, l );
//	}
//
//	  public void
//	  setNetworkEnabled(
//	      String network,
//	      boolean enabled) {
//	    List	values = getListAttributeSupport( AT_NETWORKS );
//	    boolean alreadyEnabled = values.contains(network);
//	    List	l = new ArrayList();
//
//	    if(enabled && !alreadyEnabled) {
//	      for (int i=0;i<values.size();i++){
//	        l.add(values.get(i));
//	      }
//	      l.add(network);
//	      setListAttribute( AT_NETWORKS, l );
//	    }
//	    if(!enabled && alreadyEnabled) {
//	      for (int i=0;i<values.size();i++){
//	        l.add(values.get(i));
//	      }
//	      l.remove(network);
//	      setListAttribute( AT_NETWORKS, l );
//	    }
//	  }
//
//		// peer sources
//
//	public String[]
//	getPeerSources()
//	{
//		List	values = getListAttributeSupport( AT_PEER_SOURCES );
//
//		List	res = new ArrayList();
//
//			// map back to the constants to allow == comparisons
//
//		for (int i=0;i<values.size();i++){
//
//			String	ps = (String)values.get(i);
//
//			for (int j=0;j<PEPeerSource.PS_SOURCES.length;j++){
//
//				String	x = PEPeerSource.PS_SOURCES[j];
//
//				if ( x.equals( ps )){
//
//					res.add( x );
//				}
//			}
//		}
//
//		String[]	x = new String[res.size()];
//
//		res.toArray(x);
//
//		return( x );
//	}
//
//	public boolean
//	isPeerSourceEnabled(
//		String peerSource )
//	{
//		List	values = getListAttributeSupport( AT_PEER_SOURCES );
//
//		return values.contains(peerSource);
//	}
//
//	public boolean
//	isPeerSourcePermitted(
//		String	peerSource )
//	{
//			// no DHT for private torrents or explicitly prevented
//
//		if ( peerSource.equals( PEPeerSource.PS_DHT )){
//
//			if ( 	TorrentUtils.getPrivate( torrent ) ||
//					!TorrentUtils.getDHTBackupEnabled( torrent )){
//
//				return( false );
//			}
//		}
//
//			// no PEX for private torrents
//
//		if ( peerSource.equals( PEPeerSource.PS_OTHER_PEER )){
//
//			if ( TorrentUtils.getPrivate( torrent )){
//
//				return( false );
//			}
//		}
//
//		List	values = getListAttributeSupport( AT_PEER_SOURCES_DENIED );
//
//		if ( values != null ){
//
//			if ( values.contains( peerSource )){
//
//				return( false );
//			}
//		}
//
//		return( true );
//	}
//
//	public void
//	setPeerSourcePermitted(
//		String	peerSource,
//		boolean	enabled )
//	{
//		if ( !getFlag( FLAG_ALLOW_PERMITTED_PEER_SOURCE_CHANGES )){
//
//			Logger.log(new LogEvent(torrent, LOGID, "Attempt to modify permitted peer sources denied as disabled '"
//							+ TorrentUtils.getLocalisedName(torrent) + "'"));
//
//			return;
//		}
//
//		if ( !enabled ){
//
//			setPeerSourceEnabled( peerSource, false );
//		}
//
//		List	values = getListAttributeSupport( AT_PEER_SOURCES_DENIED );
//
//		if ( values == null ){
//
//			if ( !enabled ){
//
//				values = new ArrayList();
//
//				values.add( peerSource );
//
//				setListAttribute( AT_PEER_SOURCES_DENIED, values );
//			}
//		}else{
//
//			if ( enabled ){
//
//				values.remove( peerSource );
//
//			}else{
//
//				if ( !values.contains( peerSource )){
//
//					values.add( peerSource );
//				}
//			}
//
//			setListAttribute( AT_PEER_SOURCES_DENIED, values );
//		}
//	}
//
//	public void
//	setPeerSources(
//		String[]		ps )
//	{
//		if ( ps == null ){
//
//			ps = new String[0];
//		}
//
//		List	l = new ArrayList();
//
//		for (int i=0;i<ps.length;i++){
//
//			String	p = ps[i];
//
//			if ( isPeerSourcePermitted(p)){
//
//				l.add( ps[i]);
//			}
//		}
//
//		setListAttribute( AT_PEER_SOURCES, l );
//	}
//
//	  public void
//	  setPeerSourceEnabled(
//	      String source,
//	      boolean enabled )
//	  {
//		  if ( enabled && !isPeerSourcePermitted( source )){
//
//			  return;
//		  }
//
//		  List	values = getListAttributeSupport( AT_PEER_SOURCES );
//
//		  boolean alreadyEnabled = values.contains(source);
//
//		  List	l = new ArrayList();
//
//		  if(enabled && !alreadyEnabled) {
//		    for (int i=0;i<values.size();i++){
//		      l.add(values.get(i));
//		    }
//		    l.add(source);
//		    setListAttribute( AT_PEER_SOURCES, l );
//		  }
//		  if(!enabled && alreadyEnabled) {
//		    for (int i=0;i<values.size();i++){
//		      l.add(values.get(i));
//		    }
//		    l.remove(source);
//		    setListAttribute( AT_PEER_SOURCES, l );
//		  }
//	  }
//
//
//	  // links stuff
//
//
//	public void
//	setFileLink(
//		File	link_source,
//		File	link_destination )
//	{
//		CaseSensitiveFileMap	links = getFileLinks();
//
//		File	existing = (File)links.get(link_source);
//
//		if ( link_destination == null ){
//
//			if ( existing == null ){
//
//				return;
//			}
//		}else if ( existing != null && existing.equals( link_destination )){
//
//			return;
//		}
//
//		links.put( link_source, link_destination );
//
//		List	list = new ArrayList();
//
//		Iterator	it = links.keySetIterator();
//
//		while( it.hasNext()){
//
//			File	source = (File)it.next();
//			File	target = (File)links.get(source);
//
//			String	str = source + "\n" + (target==null?"":target.toString());
//
//			list.add( str );
//		}
//
//		setListAttribute( AT_FILE_LINKS, list );
//	}
//
//	public void
//	clearFileLinks()
//	{
//		CaseSensitiveFileMap	links = getFileLinks();
//
//		List	list = new ArrayList();
//
//		Iterator	it = links.keySetIterator();
//
//		boolean	changed = false;
//
//		while( it.hasNext()){
//
//			File	source = (File)it.next();
//			File	target = (File)links.get(source);
//
//			if ( target != null ){
//
//				changed = true;
//			}
//
//			String	str = source + "\n";
//
//			list.add( str );
//		}
//
//		if ( changed ){
//
//			setListAttribute( AT_FILE_LINKS, list );
//		}
//	}
//
//	public File
//	getFileLink(
//		File	link_source )
//	{
//		return((File)getFileLinks().get(link_source));
//	}
//
//	public CaseSensitiveFileMap
//	getFileLinks()
//	{
//		List	values = getListAttributeSupport( AT_FILE_LINKS );
//
//		CaseSensitiveFileMap	res = new CaseSensitiveFileMap();
//
//		for (int i=0;i<values.size();i++){
//
//			String	entry = (String)values.get(i);
//
//			int	sep = entry.indexOf( "\n" );
//
//			if ( sep != -1 ){
//
//				File target = (sep == entry.length()-1)?null:new File( entry.substring( sep+1 ));
//
//				res.put( new File( entry.substring(0,sep)), target );
//			}
//		}
//
//		return( res );
//	}
//
//	public boolean isOurContent() {
//		// HACK!
//		Map mapAttr = getMapAttribute("Plugin.azdirector.ContentMap");
//
//		return mapAttr != null
//				&& mapAttr.containsKey("DIRECTOR PUBLISH");
//	}
//
//		// general stuff
//
//
//	protected String
//	getStringAttribute(
//		String	attribute_name )
//	{
//		informWillRead( attribute_name );
//
//		try{
//			this_mon.enter();
//
//			if ( !(attributes.get( attribute_name) instanceof byte[] )){
//
//				return( null );
//			}
//
//			byte[]	bytes = (byte[])attributes.get( attribute_name );
//
//			if ( bytes == null ){
//
//				return( null );
//			}
//
//			try{
//				return( new String( bytes, Constants.DEFAULT_ENCODING ));
//
//			}catch( UnsupportedEncodingException e ){
//
//				Debug.printStackTrace(e);
//
//				return( null );
//			}
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	protected void
//	setStringAttribute(
//		final String	attribute_name,
//		final String	attribute_value )
//	{
//		boolean	changed	= false;
//
//		try{
//			this_mon.enter();
//
//			if ( attribute_value == null ){
//
//				if ( attributes.containsKey( attribute_name )){
//
//					attributes.remove( attribute_name );
//
//					changed	= true;
//				}
//			}else{
//
//				try{
//					byte[]	existing_bytes = (byte[])attributes.get( attribute_name );
//
//					byte[]	new_bytes = attribute_value.getBytes( Constants.DEFAULT_ENCODING );
//
//					if ( 	existing_bytes == null ||
//							!Arrays.equals( existing_bytes, new_bytes )){
//
//						attributes.put( attribute_name, new_bytes );
//
//						changed	= true;
//					}
//
//				}catch( UnsupportedEncodingException e ){
//
//					Debug.printStackTrace(e);
//				}
//			}
//		}finally{
//
//			this_mon.exit();
//		}
//
//		if ( changed ){
//
//			write_required	= true;
//
//			informWritten( attribute_name );
//		}
//	}
//
//	public long
//	getLongAttribute(
//		String	attribute_name )
//	{
//		informWillRead( attribute_name );
//
//		try{
//			this_mon.enter();
//
//			Long	l = (Long)attributes.get( attribute_name );
//
//			if ( l == null ){
//
//				Object def = default_attributes.get( attribute_name );
//
//				if ( def != null ){
//
//					if ( def instanceof Long ){
//
//						return(((Long)def).longValue());
//
//					}else if ( def instanceof Integer ){
//
//						return(((Integer)def).longValue());
//
//					}else{
//
//						Debug.out( "unknown default type " + def );
//					}
//				}
//
//				return( 0 );
//			}
//
//			return( l.longValue());
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	public void
//	setLongAttribute(
//		final String	attribute_name,
//		final long		attribute_value )
//	{
//		boolean	changed	= false;
//
//		try{
//			this_mon.enter();
//
//			Long	existing_value = (Long)attributes.get( attribute_name );
//
//			if ( 	existing_value == null ||
//					existing_value.longValue() != attribute_value ){
//
//				attributes.put( attribute_name, new Long( attribute_value) );
//
//				changed	= true;
//			}
//		}finally{
//
//			this_mon.exit();
//		}
//
//		if ( changed ){
//
//			write_required	= true;
//
//			informWritten( attribute_name );
//		}
//	}
//
//	public void
//	setListAttribute(
//		String		name,
//		String[]	values )
//	{
//		List	list = values==null?null:Arrays.asList((Object[]) values.clone());
//		/*
//		if ( list != null ){
//
//			for (int i=0;i<values.length;i++){
//
//				list.add( values[i]);
//			}
//		}*/
//
//		setListAttribute( name, list );
//	}
//
//	public String getListAttribute(String name, int idx) {
//		if (name.equals(AT_NETWORKS) || name.equals(AT_PEER_SOURCES))
//			throw new UnsupportedOperationException("not supported right now, implement it yourself :P");
//
//		informWillRead(name);
//
//		try {
//			this_mon.enter();
//			List values = (List) attributes.get(name);
//			if(values == null || idx >= values.size() || idx < 0)
//				return null;
//			Object o = values.get(idx);
//			if (o instanceof byte[]) {
//				byte[] bytes = (byte[]) o;
//				String s = null;
//				try {
//					s = StringInterner.intern(new String(bytes, Constants.DEFAULT_ENCODING));
//				} catch (UnsupportedEncodingException e) {
//					Debug.printStackTrace(e);
//				}
//				if (s != null)
//					values.set(idx, s);
//				return s;
//			} else if (o instanceof String) {
//				return (String) o;
//			}
//		} finally {
//			this_mon.exit();
//		}
//
//		return null;
//	}
//
//	public String[]
//	getListAttribute(
//		String	attribute_name )
//	{
//		if ( attribute_name == AT_NETWORKS ){
//
//			return( getNetworks());
//
//		}else if ( attribute_name == AT_PEER_SOURCES ){
//
//			return( getPeerSources());
//
//		}else{
//
//			List	l = getListAttributeSupport( attribute_name );
//
//			if ( l == null ){
//
//				return( null );
//			}
//
//			String[]	res = new String[l.size()];
//
//			try {
//				res = (String[])l.toArray(res);
//			} catch (ArrayStoreException e)
//			{
//				Debug.out( "getListAttribute( " + attribute_name + ") - object isnt String - " + e );
//
//				return( null );
//			}
//
//
//			return( res );
//		}
//	}
//
//	protected List
//	getListAttributeSupport(
//		String	attribute_name )
//	{
//		informWillRead( attribute_name );
//
//		try{
//			this_mon.enter();
//
//			List	values = (List)attributes.get( attribute_name );
//
//			List	res = new ArrayList(values != null ? values.size() : 0);
//
//			if ( values != null ){
//
//				for (int i=0;i<values.size();i++){
//
//					Object	o = values.get(i);
//
//					if ( o instanceof byte[] ){
//
//						byte[]	bytes = (byte[])o;
//
//						String s = null;
//
//						try{
//
//							s = StringInterner.intern(new String( bytes, Constants.DEFAULT_ENCODING ));
//
//						}catch( UnsupportedEncodingException e ){
//
//							Debug.printStackTrace(e);
//						}
//
//						if(s != null)
//						{
//							res.add(s);
//							values.set(i, s);
//						}
//					}else if ( o instanceof String ){
//
//						res.add( o );
//					}
//				}
//			}
//
//			return( res );
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	protected void
//	setListAttribute(
//		final String	attribute_name,
//		final List		attribute_value )
//	{
//		boolean	changed	= false;
//
//		try{
//			this_mon.enter();
//
//			if ( attribute_value == null ){
//
//				if ( attributes.containsKey( attribute_name )){
//
//					attributes.remove( attribute_name );
//
//					changed	= true;
//				}
//			}else{
//
//				List old_value = getListAttributeSupport( attribute_name );
//
//				if ( old_value == null || old_value.size() != attribute_value.size()){
//
//					attributes.put( attribute_name, attribute_value );
//
//					changed	= true;
//
//				}else{
//
//					if ( old_value == attribute_value ){
//
//						Debug.out( "setListAttribute: should clone?" );
//					}
//
//					changed = !BEncoder.listsAreIdentical( old_value, attribute_value );
//
//					if ( changed ){
//
//						attributes.put( attribute_name, attribute_value );
//					}
//				}
//			}
//		}finally{
//
//			this_mon.exit();
//		}
//
//		if ( changed ){
//
//			write_required	= true;
//
//			informWritten( attribute_name );
//		}
//	}
//
//	public Map
//	getMapAttribute(
//		String	attribute_name )
//	{
//		informWillRead( attribute_name );
//
//		try{
//			this_mon.enter();
//
//			Map	value = (Map)attributes.get( attribute_name );
//
//			return( value );
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	public void
//	setMapAttribute(
//		final String	attribute_name,
//		final Map		attribute_value )
//	{
//		setMapAttribute( attribute_name, attribute_value, false );
//	}
//
//	protected void
//	setMapAttribute(
//		final String	attribute_name,
//		final Map		attribute_value,
//		boolean			disable_change_notification )
//	{
//		boolean	changed	= false;
//
//		try{
//			this_mon.enter();
//
//			if ( attribute_value == null ){
//
//				if ( attributes.containsKey( attribute_name )){
//
//					attributes.remove( attribute_name );
//
//					changed	= true;
//				}
//			}else{
//
//				Map old_value = getMapAttribute( attribute_name );
//
//				if ( old_value == null || old_value.size() != attribute_value.size()){
//
//					attributes.put( attribute_name, attribute_value );
//
//					changed	= true;
//
//				}else{
//
//					if ( old_value == attribute_value ){
//
//						Debug.out( "setMapAttribute: should clone?" );
//					}
//
//					changed = !BEncoder.mapsAreIdentical( old_value, attribute_value );
//
//					if ( changed ){
//
//						attributes.put( attribute_name, attribute_value );
//					}
//				}
//			}
//		}finally{
//
//			this_mon.exit();
//		}
//
//		if ( changed && !disable_change_notification ){
//
//			write_required	= true;
//
//			informWritten( attribute_name );
//		}
//	}
//
//	public boolean
//	hasAttribute(
//		String name )
//	{
//		try{
//
//			this_mon.enter();
//
//			if ( attributes == null) {return false;}
//
//			return attributes.containsKey(name);
//
//		}finally{
//
//			this_mon.exit();
//		}
//	}
//
//	// These methods just use long attributes to store data into.
//
//	public void
//	setIntAttribute(
//		String 	name,
//		int 	value)
//	{
//		setLongAttribute(name, value);
//	}
//
//	public int
//	getIntAttribute(
//		String name )
//	{
//		return (int)getLongAttribute(name);
//	}
//
//	public void
//	setBooleanAttribute(
//		String 		name,
//		boolean 	value )
//	{
//		setLongAttribute(name, (value ? 1 : 0));
//	}
//
//	public boolean
//	getBooleanAttribute(
//		String name )
//	{
//		return getLongAttribute(name) != 0;
//	}
//
//
//	public static DownloadManagerState
//	getDownloadState(
//		DownloadManager	dm )
//	{
//		return( new nullState(dm));
//	}
//
//	protected void
//	informWritten(
//		final String		attribute_name )
//	{
//		List	listeners_ref = listeners_cow.getList();
//
//		for (int i=0;i<listeners_ref.size();i++){
//
//			try{
//				((DownloadManagerStateListener)listeners_ref.get(i)).stateChanged(
//					this,
//					new DownloadManagerStateEvent()
//					{
//						public int
//						getType()
//						{
//							return( DownloadManagerStateEvent.ET_ATTRIBUTE_WRITTEN );
//						}
//
//						public Object
//						getData()
//						{
//							return( attribute_name );
//						}
//					});
//			}catch( Throwable e ){
//
//				Debug.printStackTrace(e);
//			}
//		}
//
//		listeners_ref = null;
//		CopyOnWriteList write_listeners = (CopyOnWriteList)listeners_write_map_cow.get(attribute_name);
//		if (write_listeners != null) {listeners_ref = write_listeners.getList();}
//
//		if (listeners_ref != null) {
//			for (int i=0;i<listeners_ref.size();i++) {
//				try {((DownloadManagerStateAttributeListener)listeners_ref.get(i)).attributeEventOccurred(download_manager, attribute_name, DownloadManagerStateAttributeListener.WRITTEN);}
//				catch (Throwable t) {Debug.printStackTrace(t);}
//			}
//		}
//
//
//	}
//
//	protected void
//	informWillRead(
//		final String		attribute_name )
//	{
//			// avoid potential recursion will a will-be-read causing a write that then
//			// causes a further will-be-read...
//
//		List	will_be_read_list = (List)tls_wbr.get();
//
//		if ( !will_be_read_list.contains( attribute_name )){
//
//			will_be_read_list.add( attribute_name );
//
//			try{
//
//				List	listeners_ref = listeners_cow.getList();
//
//				for (int i=0;i<listeners_ref.size();i++){
//
//					try{
//						((DownloadManagerStateListener)listeners_ref.get(i)).stateChanged(
//							this,
//							new DownloadManagerStateEvent()
//							{
//								public int
//								getType()
//								{
//									return( DownloadManagerStateEvent.ET_ATTRIBUTE_WILL_BE_READ );
//								}
//
//								public Object
//								getData()
//								{
//									return( attribute_name );
//								}
//							});
//					}catch( Throwable e ){
//
//						Debug.printStackTrace(e);
//					}
//				}
//
//				listeners_ref = null;
//				CopyOnWriteList read_listeners = null;
//
//				read_listeners = (CopyOnWriteList)listeners_read_map_cow.get(attribute_name);
//				if (read_listeners != null) {listeners_ref = read_listeners.getList();}
//
//				if (listeners_ref != null) {
//					for (int i=0;i<listeners_ref.size();i++) {
//						try {((DownloadManagerStateAttributeListener)listeners_ref.get(i)).attributeEventOccurred(download_manager, attribute_name, DownloadManagerStateAttributeListener.WILL_BE_READ);}
//						catch (Throwable t) {Debug.printStackTrace(t);}
//					}
//				}
//			}finally{
//
//				will_be_read_list.remove( attribute_name );
//			}
//		}
//	}
//
//	public void
//	addListener(
//		DownloadManagerStateListener	l )
//	{
//		listeners_cow.add( l );
//	}
//
//	public void
//	removeListener(
//		DownloadManagerStateListener	l )
//	{
//		listeners_cow.remove(l);
//	}
//
//	public void addListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {
//		CopyOnWriteMap map_to_use = (event_type == DownloadManagerStateAttributeListener.WILL_BE_READ) ? this.listeners_read_map_cow : this.listeners_write_map_cow;
//		CopyOnWriteList lst = (CopyOnWriteList)map_to_use.get(attribute);
//		if (lst == null) {
//			lst = new CopyOnWriteList();
//			map_to_use.put(attribute, lst);
//		}
//		lst.add(l);
//	}
//
//	public void removeListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {
//		CopyOnWriteMap map_to_use = (event_type == DownloadManagerStateAttributeListener.WILL_BE_READ) ? this.listeners_read_map_cow : this.listeners_write_map_cow;
//		CopyOnWriteList lst = (CopyOnWriteList)map_to_use.get(attribute);
//		if (lst != null) {lst.remove(l);}
//	}
//
//	public void
//	generateEvidence(
//		IndentWriter writer)
//	{
//		writer.println( "DownloadManagerState" );
//
//		try{
//			writer.indent();
//
//			writer.println( "parameters=" + parameters );
//
//			writer.println("primary file=" + Debug.secretFileName(getPrimaryFile()));
//
//		}finally{
//
//			writer.exdent();
//		}
//	}
//
//	protected static class
//	nullState
//		implements DownloadManagerState
//	{
//
//		protected DownloadManager		download_manager;
//
//		protected
//		nullState(
//			DownloadManager	_dm )
//		{
//			download_manager = _dm;
//		}
//
//		public TOTorrent
//		getTorrent()
//		{
//			return( null );
//		}
//
//		public File
//		getStateFile( )
//		{
//			return( null );
//		}
//
//		public DownloadManager
//		getDownloadManager()
//		{
//			return( download_manager );
//		}
//
//		public void
//		clearResumeData()
//		{
//		}
//
//		public Map
//		getResumeData()
//		{
//			return( new HashMap());
//		}
//
//		public void
//		setResumeData(
//			Map	data )
//		{
//		}
//
//		public boolean
//		isResumeDataComplete()
//		{
//			return( false );
//		}
//
//		public void
//		clearTrackerResponseCache()
//		{
//		}
//
//		public Map
//		getTrackerResponseCache()
//		{
//			return( new HashMap());
//		}
//
//		public void
//		setTrackerResponseCache(
//			Map		value )
//		{
//		}
//
//		public void
//		setFlag(
//			long		flag,
//			boolean		set )
//		{
//		}
//
//		public boolean
//		getFlag(
//			long		flag )
//		{
//			return( false );
//		}
//
//		public long
//		getFlags()
//		{
//			return 0;
//		}
//
//		public void
//		setParameterDefault(
//			String	name )
//		{
//		}
//
//		public long
//		getLongParameter(
//			String	name )
//		{
//			return( 0 );
//		}
//
//		public void
//		setLongParameter(
//			String	name,
//			long	value )
//		{
//		}
//
//		public int
//		getIntParameter(
//			String	name )
//		{
//			return( 0 );
//		}
//
//		public void
//		setIntParameter(
//			String	name,
//			int		value )
//		{
//		}
//
//		public boolean
//		getBooleanParameter(
//			String	name )
//		{
//			return( false );
//		}
//
//		public void
//		setBooleanParameter(
//			String		name,
//			boolean		value )
//		{
//		}
//
//		public void
//		setAttribute(
//			String		name,
//			String		value )
//		{
//		}
//
//		public String
//		getAttribute(
//			String		name )
//		{
//			return( null );
//		}
//
//		public String
//		getTrackerClientExtensions()
//		{
//			return( null );
//		}
//
//		public void
//		setTrackerClientExtensions(
//			String		value )
//		{
//		}
//
//		public void
//		setListAttribute(
//			String		name,
//			String[]	values )
//		{
//		}
//
//
//		public String getListAttribute(String name, int idx) {
//			return null;
//		}
//
//		public String[]
//		getListAttribute(
//			String	name )
//		{
//			return( null );
//		}
//
//		public void
//		setMapAttribute(
//			String		name,
//			Map			value )
//		{
//		}
//
//		public Map
//		getMapAttribute(
//			String		name )
//		{
//			return( null );
//		}
//
//		public boolean hasAttribute(String name) {return false;}
//		public int getIntAttribute(String name) {return 0;}
//		public long getLongAttribute(String name) {return 0L;}
//		public boolean getBooleanAttribute(String name) {return false;}
//		public void setIntAttribute(String name, int value) {}
//		public void setLongAttribute(String name, long value) {}
//		public void setBooleanAttribute(String name, boolean value) {}
//
//		public Category
//		getCategory()
//		{
//			return( null );
//		}
//
//		public void
//		setCategory(
//			Category cat )
//		{
//		}
//
//		public String[]
//		getNetworks()
//		{
//			return( new String[0] );
//		}
//
//
//	    public boolean isNetworkEnabled(String network) {
//	      return false;
//	    }
//
//		public void
//		setNetworks(
//			String[]		networks )
//		{
//		}
//
//
//	    public void setNetworkEnabled(
//	        String network,
//	        boolean enabled) {
//	    }
//
//		public String[]
//		getPeerSources()
//		{
//			return( new String[0] );
//		}
//		public boolean
//		isPeerSourcePermitted(
//			String	peerSource )
//		{
//			return( false );
//		}
//
//		public void setPeerSourcePermitted(String peerSource, boolean permitted) {
//		}
//
//	    public boolean
//	    isPeerSourceEnabled(
//	        String peerSource) {
//	      return false;
//	    }
//
//	    public void suppressStateSave(boolean suppress) {}
//
//		public void
//		setPeerSources(
//			String[]		networks )
//		{
//		}
//
//
//	    public void
//	    setPeerSourceEnabled(
//	        String source,
//	        boolean enabled) {
//	    }
//
//	    public void
//		setFileLink(
//			File	link_source,
//			File	link_destination )
//	    {
//	    }
//		public void
//		clearFileLinks()
//		{
//		}
//
//		public File
//		getFileLink(
//			File	link_source )
//		{
//			return( null );
//		}
//
//		public CaseSensitiveFileMap
//		getFileLinks()
//		{
//			return( new CaseSensitiveFileMap());
//		}
//
//		public void
//		setActive(boolean active )
//		{
//		}
//
//		public void discardFluff() {}
//
//		public void
//		save()
//		{
//		}
//
//		public void
//		delete()
//		{
//		}
//
//		public void
//		addListener(
//			DownloadManagerStateListener	l )
//		{}
//
//		public void
//		removeListener(
//			DownloadManagerStateListener	l )
//		{}
//
//		public void addListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {}
//		public void removeListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {}
//
//        public void setDisplayName(String name) {}
//        public String getDisplayName() {return null;}
//
//        public void setUserComment(String name) {}
//        public String getUserComment() {return null;}
//
//        public void setRelativeSavePath(String name) {}
//        public String getRelativeSavePath() {return null;}
//
//		public boolean parameterExists(String name) {
//			// TODO Auto-generated method stub
//			return false;
//		}
//
//		public void
//		generateEvidence(
//			IndentWriter writer)
//		{
//			writer.println( "DownloadManagerState: broken torrent" );
//		}
//
//		public boolean isOurContent() {
//			// TODO Auto-generated method stub
//			return false;
//		}
//
//		// @see org.gudy.azureus2.core3.download.DownloadManagerState#getPrimaryFile()
//
//		public String getPrimaryFile() {
//			// TODO Auto-generated method stub
//			return null;
//		}
//
//		// @see org.gudy.azureus2.core3.download.DownloadManagerState#setPrimaryFile(java.lang.String)
//
//		public void setPrimaryFile(String relativeFile) {
//			// TODO Auto-generated method stub
//
//		}
//	}
//
//	protected static class
//	CachedStateWrapper
//		extends 	LogRelation
//		implements 	TorrentUtils.ExtendedTorrent
//	{
//		private DownloadManagerImpl	download_manager;
//
//		private String		torrent_file;
//		private HashWrapper	torrent_hash_wrapper;
//		private Map			cache;
//		private Map			cache_attributes;
//		private Map			cache_azp;
//
//		private volatile TorrentUtils.ExtendedTorrent		delegate;
//		private TOTorrentException							fixup_failure;
//
//		private boolean		discard_pieces;
//		private boolean		logged_failure;
//
//		private Boolean		simple_torrent;
//		private long		size;
//
//		private URL										announce_url;
//		private cacheGroup								announce_group;
//
//		private volatile boolean		discard_fluff;
//
//		protected
//		CachedStateWrapper(
//			DownloadManagerImpl		_download_manager,
//			String					_torrent_file,
//			byte[]					_torrent_hash,
//			Map						_cache,
//			boolean					_force_piece_discard )
//		{
//			download_manager		= _download_manager;
//			torrent_file			= _torrent_file;
//			torrent_hash_wrapper	= new HashWrapper( _torrent_hash );
//			cache					= _cache;
//
//			cache_attributes 	= (Map)cache.get( "attributes" );
//			cache_azp 			= (Map)cache.get( "azp" );
//
//			if ( _force_piece_discard ){
//
//				discard_pieces	= true;
//
//			}else{
//
//				Long	l_fp = (Long)cache.get( "dp" );
//
//				if ( l_fp != null ){
//
//					discard_pieces = l_fp.longValue() == 1;
//				}
//			}
//
//			Long	st = (Long)cache.get( "simple" );
//
//			if ( st != null ){
//
//				simple_torrent = new Boolean( st.longValue()==1 );
//			}
//
//			Long	l_size = (Long)cache.get( "size" );
//
//			if ( l_size != null ){
//
//				size = l_size.longValue();
//			}
//
//			byte[]	au = (byte[])cache.get( "au" );
//
//			if ( au != null ){
//
//				try{
//					announce_url = StringInterner.internURL(new URL((new String( au, "UTF-8" ))));
//
//				}catch( Throwable e ){
//
//				}
//			}
//
//			List	ag = (List)cache.get( "ag" );
//
//			if ( ag != null ){
//
//				try{
//					announce_group = importGroup( ag );
//
//				}catch( Throwable e ){
//
//				}
//			}
//		}
//
//		protected static Map
//		export(
//			DownloadManagerState dms )
//
//			throws TOTorrentException
//		{
//			Map	cache = new HashMap();
//
//			TOTorrent	state = dms.getTorrent();
//
//			cache.put( "hash", state.getHash());
//			cache.put( "name", state.getName());
//			cache.put( "comment", state.getComment());
//			cache.put( "createdby", state.getCreatedBy());
//			cache.put( "size", new Long( state.getSize()));
//
//			cache.put( "encoding", state.getAdditionalStringProperty( "encoding" ));
//			cache.put( "torrent filename", state.getAdditionalStringProperty( "torrent filename" ));
//
//			cache.put( "attributes", state.getAdditionalMapProperty( ATTRIBUTE_KEY ));
//			cache.put( "azp", state.getAdditionalMapProperty( AZUREUS_PROPERTIES_KEY ));
//
//			try{
//				cache.put( "au", state.getAnnounceURL().toExternalForm());
//				cache.put( "ag", exportGroup(state.getAnnounceURLGroup()));
//
//			}catch( Throwable e ){
//			}
//
//			boolean	discard_pieces = dms.isResumeDataComplete();
//
//			TOTorrent	t = dms.getTorrent();
//
//			if ( t instanceof CachedStateWrapper ){
//
//				CachedStateWrapper csw = (CachedStateWrapper)t;
//
//				if ( !discard_pieces ){
//
//						// discard pieces if they are currently discarded
//
//					discard_pieces = csw.peekPieces() == null;
//				}
//
//				Boolean	simple_torrent = csw.simple_torrent;
//
//				if ( simple_torrent != null ){
//
//					cache.put( "simple", new Long(simple_torrent.booleanValue()?1:0 ));
//				}
//			}else{
//
//				// Debug.out( "Hmm, torrent isn't cache-state-wrapper, it is " + t );
//			}
//
//			cache.put( "dp", new Long( discard_pieces?1:0 ));
//
//			return( cache );
//		}
//
//		protected static List
//		exportGroup(
//			TOTorrentAnnounceURLGroup		group )
//		{
//			TOTorrentAnnounceURLSet[]	sets = group.getAnnounceURLSets();
//
//			List	result = new ArrayList();
//
//			for (int i=0;i<sets.length;i++){
//
//				TOTorrentAnnounceURLSet	set = sets[i];
//
//				URL[]	urls = set.getAnnounceURLs();
//
//				if ( urls.length > 0 ){
//
//					List	s = new ArrayList( urls.length );
//
//					for (int j=0;j<urls.length;j++){
//
//						s.add( urls[j].toExternalForm());
//					}
//
//					result.add( s );
//				}
//			}
//
//			return( result );
//		}
//
//		protected cacheGroup
//		importGroup(
//			List		l )
//
//			throws Exception
//		{
//			return( new cacheGroup( l ));
//		}
//
//		protected class
//		cacheGroup
//			implements TOTorrentAnnounceURLGroup
//		{
//			private TOTorrentAnnounceURLSet[]		sets;
//
//			protected
//			cacheGroup(
//				List	group )
//
//				throws Exception
//			{
//				sets = new TOTorrentAnnounceURLSet[ group.size() ];
//
//				for (int i = 0; i < sets.length; i++){
//
//					List set = (List) group.get(i);
//
//					URL[] urls = new URL[set.size()];
//
//					for (int j = 0; j < urls.length; j++){
//
//						urls[j] = StringInterner.internURL(new URL(new String((byte[]) set.get(j), "UTF-8")));
//					}
//
//					sets[i] = new cacheSet(urls);
//				}
//			}
//
//			public TOTorrentAnnounceURLSet[]
//           	getAnnounceURLSets()
//			{
//				if ( announce_group == null && fixup()){
//
//					return delegate.getAnnounceURLGroup().getAnnounceURLSets();
//				}
//
//				return( sets );
//			}
//
//			void fixGroup()
//			{
//				TOTorrentAnnounceURLSet[] realSets = delegate.getAnnounceURLGroup().getAnnounceURLSets();
//
//				if ( realSets.length != sets.length ){
//
//					Debug.out("Cached announce group state does not match real state");
//
//				}else{
//
//					for (int i=0;i<realSets.length;i++){
//
//						if ( sets[i] instanceof cacheSet ){
//
//							((cacheSet)sets[i]).delegateSet = realSets[i];
//						}
//					}
//
//					sets = null;
//				}
//			}
//
//           	public void
//           	setAnnounceURLSets(
//           		TOTorrentAnnounceURLSet[]	toSet )
//           	{
//     	   		if ( fixup()){
//
//     	   			TOTorrentAnnounceURLSet[] modToSet = new TOTorrentAnnounceURLSet[toSet.length];
//
//               		for (int i = 0; i < toSet.length; i++){
//
//						TOTorrentAnnounceURLSet set = toSet[i];
//
//						if ( set instanceof cacheSet ){
//
//							modToSet[i] = ((cacheSet) set).delegateSet;
//						}
//
//						if ( modToSet[i] == null ){
//
//							modToSet[i] = set;
//						}
//					}
//
//    				delegate.getAnnounceURLGroup().setAnnounceURLSets( modToSet );
//    			}
//           	}
//
//           	public TOTorrentAnnounceURLSet
//           	createAnnounceURLSet(
//           		URL[]	urls )
//           	{
//           		if ( fixup()){
//
//    				return( delegate.getAnnounceURLGroup().createAnnounceURLSet( urls ));
//    			}
//
//           		return( null );
//           	}
//
//           	protected class
//           	cacheSet
//           		implements TOTorrentAnnounceURLSet
//           	{
//           		private URL[]					urls;
//           		private TOTorrentAnnounceURLSet delegateSet;
//
//				public cacheSet(URL[] urls)
//				{
//					this.urls = urls;
//				}
//
//           		public URL[]
//           		getAnnounceURLs()
//           		{
//           			if ( announce_group == null && fixup() && delegateSet != null ){
//
//           				return delegateSet.getAnnounceURLs();
//           			}
//
//           			return( urls );
//           		}
//
//		    	public void
//		    	setAnnounceURLs(
//		    		URL[]		toSet )
//		    	{
//		    		if ( fixup() && delegateSet != null ){
//
//		    			delegateSet.setAnnounceURLs( toSet );
//
//		    		}else{
//
//		    			urls = toSet;
//		    		}
//		    	}
//           	}
//		}
//
//		protected void
//		clearCache()
//		{
//			cache	= null;
//		}
//
//		protected boolean
//		fixup()
//		{
//			try{
//				if ( delegate == null ){
//
//					synchronized( this ){
//
//						if ( delegate == null ){
//
//							// System.out.println( "Fixing up " + this );
//
//							if ( fixup_failure != null ){
//
//								throw( fixup_failure );
//							}
//
//							delegate = loadRealState();
//
//							if ( discard_fluff ){
//
//								delegate.setDiscardFluff( discard_fluff );
//							}
//
//							if ( cache != null ){
//
//								Debug.out( "Cache miss forced fixup" );
//							}
//
//							cache = null;
//
//								// join cache view back up with real state to save memory as the one
//								// we've just read is irrelevant due to the cache values being
//								// used
//
//							if ( cache_attributes != null ){
//
//								delegate.setAdditionalMapProperty( ATTRIBUTE_KEY, cache_attributes );
//
//								cache_attributes = null;
//							}
//
//							if ( cache_azp != null ){
//
//								delegate.setAdditionalMapProperty( AZUREUS_PROPERTIES_KEY, cache_azp );
//
//								cache_azp = null;
//							}
//
//							announce_url = null;
//
//							if ( announce_group != null ){
//
//								announce_group.fixGroup();
//
//								announce_group = null;
//							}
//						}
//					}
//				}
//
//				return( true );
//
//			}catch( TOTorrentException e ){
//
//				fixup_failure	= e;
//
//				if ( download_manager != null ){
//
//					download_manager.setTorrentInvalid( Debug.getNestedExceptionMessage( e ));
//
//				}else{
//
//					if ( !logged_failure ){
//
//						logged_failure = true;
//
//						Debug.out( "Torrent can't be loaded: " + Debug.getNestedExceptionMessage( e ));
//					}
//				}
//			}
//
//			return( false );
//		}
//
//		protected TorrentUtils.ExtendedTorrent
//		loadRealState()
//
//			throws TOTorrentException
//		{
//			// System.out.println("loadReal: " + torrent_file + " dp=" + discard_pieces + ": " + Debug.getCompressedStackTrace().substring(114));
//
//			if ( !SUPPRESS_FIXUP_ERRORS && Constants.isCVSVersion() ){
//
//				if ( Thread.currentThread().isDaemon()){
//
//					// Debug.outNoStack( "Fixup on thread " + Thread.currentThread().getName() + ": " + Debug.getCompressedStackTrace());
//
//				}else{
//					Debug.out( "Premature fixup?" );
//				}
//			}
//
//			File	saved_file = getStateFile( torrent_hash_wrapper.getBytes() );
//
//			if ( saved_file.exists()){
//
//				try{
//
//					return( TorrentUtils.readDelegateFromFile( saved_file, discard_pieces ));
//
//				}catch( Throwable e ){
//
//					Debug.out( "Failed to load download state for " + saved_file );
//				}
//			}
//
//				// try reading from original
//
//			TOTorrent original_torrent = TorrentUtils.readFromFile( new File(torrent_file), true );
//
//			torrent_hash_wrapper = original_torrent.getHashWrapper();
//
//			saved_file = getStateFile( torrent_hash_wrapper.getBytes());
//
//			if ( saved_file.exists()){
//
//				try{
//					return( TorrentUtils.readDelegateFromFile( saved_file, discard_pieces ));
//
//				}catch( Throwable e ){
//
//					Debug.out( "Failed to load download state for " + saved_file );
//				}
//			}
//
//				// we must copy the torrent as we want one independent from the
//				// original (someone might still have references to the original
//				// and do stuff like write it somewhere else which would screw us
//				// up)
//
//			TorrentUtils.copyToFile( original_torrent, saved_file );
//
//			return( TorrentUtils.readDelegateFromFile( saved_file, discard_pieces ));
//		}
//
//
//		public byte[]
//    	getName()
//		{
//			Map	c = cache;
//
//			if ( c != null ){
//
//				byte[] name = (byte[])c.get( "name" );
//				if (name != null) {
//					return name;
//				}
//			}
//
//	   		if ( fixup()){
//
//				return( delegate.getName());
//			}
//
//	   		// Does grabbing the nested exception message always give us something useful?
//	   		// My experience is that we just get an empty string here...
//	   		return(("Error - " + Debug.getNestedExceptionMessage( fixup_failure )).getBytes());
//    	}
//
//    	public boolean
//    	isSimpleTorrent()
//    	{
//    		if ( simple_torrent != null ){
//
//    			return( simple_torrent.booleanValue());
//    		}
//
//    		if ( fixup()){
//
//    			boolean st = delegate.isSimpleTorrent();
//
//    			simple_torrent = new Boolean( st );
//
//    			return( st );
//    		}
//
//    		return( false );
//    	}
//
//    	public byte[]
//    	getComment()
//    	{
//			Map	c = cache;
//
//			if ( c != null ){
//
//				return((byte[])c.get( "comment" ));
//			}
//
//	   		if ( fixup()){
//
//				return( delegate.getComment());
//			}
//
//	   		return( null );
//    	}
//
//    	public void
//    	setComment(
//    		String		comment )
//       	{
//	   		if ( fixup()){
//
//				delegate.setComment( comment );
//			}
//    	}
//
//    	public long
//    	getCreationDate()
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getCreationDate());
//			}
//
//	   		return( 0 );
//    	}
//
//    	public void
//    	setCreationDate(
//    		long		date )
//       	{
//	   		if ( fixup()){
//
//				delegate.setCreationDate( date );
//			}
//    	}
//
//    	public byte[]
//    	getCreatedBy()
//       	{
//			Map	c = cache;
//
//			if ( c != null ){
//
//				return((byte[])c.get( "createdby" ));
//			}
//
//	   		if ( fixup()){
//
//				return( delegate.getCreatedBy());
//			}
//
//	   		return( null );
//    	}
//
//       	public void
//    	setCreatedBy(
//    		byte[]		cb )
//       	{
//	   		if ( fixup()){
//
//				delegate.setCreatedBy( cb );
//			}
//    	}
//
//    	public boolean
//    	isCreated()
//       	{
//	   		if ( fixup()){
//
//				return( delegate.isCreated());
//			}
//
//	   		return( false );
//    	}
//
//    	public URL
//    	getAnnounceURL()
//       	{
//    		if ( announce_url != null ){
//
//    			return( announce_url );
//    		}
//
//	   		if ( fixup()){
//
//				return( delegate.getAnnounceURL());
//			}
//
//	   		return( null );
//    	}
//
//    	public boolean
//    	setAnnounceURL(
//    		URL		url )
//       	{
//
//
//	   		if ( fixup()){
//				return( delegate.setAnnounceURL( url ));
//			} else
//				announce_url = url;
//
//
//	   		return( false );
//    	}
//
//    	public TOTorrentAnnounceURLGroup
//    	getAnnounceURLGroup()
//       	{
//    		if ( announce_group != null ){
//
//    			return( announce_group );
//    		}
//
//	   		if ( fixup()){
//
//				return( delegate.getAnnounceURLGroup());
//			}
//
//	   		return( null );
//    	}
//
//    	public byte[][]
//    	getPieces()
//
//    		throws TOTorrentException
//	   	{
//	   		if ( fixup()){
//
//				return( delegate.getPieces());
//			}
//
//	   		throw( fixup_failure );
//    	}
//
//
//    	public void
//    	setPieces(
//    		byte[][]	pieces )
//
//    		throws TOTorrentException
//	   	{
//	   		if ( fixup()){
//
//				delegate.setPieces( pieces );
//
//				return;
//			}
//
//	   		throw( fixup_failure );
//    	}
//
//    	public byte[][]
//    	peekPieces()
//
//    		throws TOTorrentException
//    	{
//    		if ( fixup()){
//
//    			return( delegate.peekPieces());
//    		}
//
//	   		throw( fixup_failure );
//    	}
//
//    	public void
//    	setDiscardFluff(
//    		boolean discard )
//    	{
//    		discard_fluff	= discard;
//
//    		if ( delegate != null ){
//
//    			delegate.setDiscardFluff( discard_fluff );
//    		}
//     	}
//
//    	public long
//    	getPieceLength()
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getPieceLength());
//			}
//
//	   		return( 0 );
//       	}
//
//		public int
//    	getNumberOfPieces()
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getNumberOfPieces());
//			}
//
//	   		return( 0 );
//    	}
//
//    	public long
//    	getSize()
//       	{
//    		if ( size > 0 ){
//
//    			return( size );
//    		}
//
//	   		if ( fixup()){
//
//				size = delegate.getSize();
//
//				return( size );
//			}
//
//	   		return( 0 );
//    	}
//
//    	public TOTorrentFile[]
//    	getFiles()
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getFiles());
//			}
//
//	   		return( new TOTorrentFile[0] );
//    	}
//
//    	public byte[]
//    	getHash()
//
//    		throws TOTorrentException
//	   	{
//    			// optimise this
//
//    		return( torrent_hash_wrapper.getBytes());
//    	}
//
//    	public HashWrapper
//    	getHashWrapper()
//
//    		throws TOTorrentException
//	   	{
//    		return( torrent_hash_wrapper );
//    	}
//
//    	public void
//    	setHashOverride(
//    		byte[] hash )
//
//    		throws TOTorrentException
//    	{
//    		throw( new TOTorrentException( "Not supported", TOTorrentException.RT_HASH_FAILS ));
//    	}
//
//    	public boolean
//    	hasSameHashAs(
//    		TOTorrent		other )
//       	{
//    		try{
//    			byte[]	other_hash = other.getHash();
//
//    			return( Arrays.equals( getHash(), other_hash ));
//
//    		}catch( TOTorrentException e ){
//
//    			Debug.printStackTrace( e );
//
//    			return( false );
//    		}
//       	}
//
//    	public boolean
//    	getPrivate()
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getPrivate());
//			}
//
//	   		return( false );
//    	}
//
//    	public void
//    	setPrivate(
//    		boolean	_private )
//
//    		throws TOTorrentException
//    	   	{
//    	   		if ( fixup()){
//
//    				delegate.setPrivate( _private );
//    			}
//        	}
//
//    	public void
//    	setAdditionalStringProperty(
//    		String		name,
//    		String		value )
//       	{
//	   		if ( fixup()){
//
//				delegate.setAdditionalStringProperty( name, value );
//			}
//    	}
//
//    	public String
//    	getAdditionalStringProperty(
//    		String		name )
//       	{
//			Map	c = cache;
//
//			if ( c != null && ( name.equals( "encoding") || name.equals( "torrent filename" ))){
//
//				byte[] res = (byte[])c.get( name );
//
//				if ( res == null ){
//
//					return( null );
//				}
//
//				try{
//					return( new String( res, "UTF8" ));
//
//				}catch( Throwable e ){
//
//					Debug.printStackTrace( e );
//
//					return( null );
//				}
//			}
//
//	   		if ( fixup()){
//
//				return( delegate.getAdditionalStringProperty( name ));
//			}
//
//	   		return( null );
//    	}
//
//    	public void
//    	setAdditionalByteArrayProperty(
//    		String		name,
//    		byte[]		value )
//       	{
//	   		if ( fixup()){
//
//				delegate.setAdditionalByteArrayProperty( name, value );
//			}
//    	}
//
//    	public byte[]
//    	getAdditionalByteArrayProperty(
//    		String		name )
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getAdditionalByteArrayProperty( name ));
//			}
//
//	   		return( null );
//    	}
//
//    	public void
//    	setAdditionalLongProperty(
//    		String		name,
//    		Long		value )
//       	{
//	   		if ( fixup()){
//
//				delegate.setAdditionalLongProperty( name, value );
//			}
//    	}
//
//    	public Long
//    	getAdditionalLongProperty(
//    		String		name )
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getAdditionalLongProperty( name ));
//			}
//
//	   		return( null );
//    	}
//
//
//    	public void
//    	setAdditionalListProperty(
//    		String		name,
//    		List		value )
//       	{
//	   		if ( fixup()){
//
//				delegate.setAdditionalListProperty( name, value );
//			}
//    	}
//
//    	public List
//    	getAdditionalListProperty(
//    		String		name )
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getAdditionalListProperty( name ));
//			}
//
//	   		return( null );
//    	}
//
//    	public void
//    	setAdditionalMapProperty(
//    		String		name,
//    		Map			value )
//       	{
//	   		if ( fixup()){
//
//				delegate.setAdditionalMapProperty( name, value );
//			}
//    	}
//
//    	public Map
//    	getAdditionalMapProperty(
//    		String		name )
//       	{
//			Map	c = cache_attributes;
//
//			if ( c != null &&  name.equals( "attributes" )){
//
//				return( c );
//			}
//
//			c = cache_azp;
//
//			if ( c != null &&  name.equals( "azureus_properties" )){
//
//				return( c );
//			}
//
//	   		if ( fixup()){
//
//				return( delegate.getAdditionalMapProperty( name ));
//			}
//
//	   		return( null );
//    	}
//
//    	public Object
//    	getAdditionalProperty(
//    		String		name )
//       	{
//	   		if ( fixup()){
//
//				return( delegate.getAdditionalProperty( name ));
//			}
//
//	   		return( null );
//    	}
//
//    	public void
//    	setAdditionalProperty(
//    		String		name,
//    		Object		value )
//       	{
//	   		if ( fixup()){
//
//				delegate.setAdditionalProperty( name, value );
//			}
//    	}
//
//    	public void
//    	removeAdditionalProperty(
//    		String name )
//       	{
//	   		if ( fixup()){
//
//				delegate.removeAdditionalProperty( name );
//			}
//    	}
//
//    	public void
//    	removeAdditionalProperties()
//       	{
//	   		if ( fixup()){
//
//				delegate.removeAdditionalProperties();
//			}
//    	}
//
//    	public void
//    	serialiseToBEncodedFile(
//    		File		file )
//
//    		throws TOTorrentException
//	   	{
//	   		if ( fixup()){
//
//				delegate.serialiseToBEncodedFile( file );
//
//				return;
//			}
//
//	   		throw( fixup_failure );
//    	}
//
//    	public Map
//    	serialiseToMap()
//
//    		throws TOTorrentException
//	   	{
//	   		if ( fixup()){
//
//				return( delegate.serialiseToMap());
//			}
//
//	   		throw( fixup_failure );
//    	}
//
//       public void
//       serialiseToXMLFile(
//    	   File		file )
//
//    	   throws TOTorrentException
//   	   	{
//   	   		if ( fixup()){
//
//   				delegate.serialiseToXMLFile( file );
//
//   				return;
//   			}
//
//   	   		throw( fixup_failure );
//       	}
//
//       public AEMonitor
//       getMonitor()
//      	{
//	   		if ( fixup()){
//
//				return( delegate.getMonitor());
//			}
//
//	   		return( null );
//      	}
//
//       public void
//       print()
//      	{
//	   		if ( fixup()){
//
//				delegate.print();
//			}
//      	}
//
//     	/* (non-Javadoc)
//     	 * @see org.gudy.azureus2.core3.logging.LogRelation#getLogRelationText()
//     	 */
//     	public String getRelationText() {
//     		return "Torrent: '" + new String(getName()) + "'";
//     	}
//
//     	/* (non-Javadoc)
//     	 * @see org.gudy.azureus2.core3.logging.LogRelation#queryForClass(java.lang.Class)
//     	 */
//     	public Object[] getQueryableInterfaces() {
//     		// yuck
//     		try {
//     			return new Object[] { AzureusCoreFactory.getSingleton()
//     					.getGlobalManager().getDownloadManager(this) };
//     		} catch (Exception e) {
//     		}
//
//     		return null;
//     	}
//	}
}